package socket.auction.client;


import java.io.*;
import java.net.*;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Scanner;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.util.encoders.Hex;

import rmi.domain.*;
import socket.domain.Messagepk;
import socket.domain.SplittLine;

public class Client implements Runnable{
	private static boolean stop=true;
	private static int udp=0;
	private static String username="";
	private DatagramSocket udpclientSocket=null;
	private Key key;
	public Client(){
		//Thread fuer Notificationlistener
		Thread udpClientThread = new Thread(this);
		readKeyfile("alice");
		//udpClientThread.start(); ---Deactivation of UDP Notification

	}


	public static void main(String[] args) throws Exception{
		//Parameter
		String host = "";
		String tcpport ="";
		String udpport = "";

		if (args.length==3){
			host = args[0];
			tcpport = args[1];
			udpport = args[2];
		}
		if (chkparam(host, tcpport, udpport)){
			//Instanz Notifylistener
			Client client = new Client();

			String sentence;
			String linestart=">";
			int tcp = Integer.parseInt(tcpport);
			udp = Integer.parseInt(udpport);

			//TCP Socket und I/OStreams
			Socket clientSocket = null;
			try{
				clientSocket = new Socket(host,tcp);
			}
			catch(ConnectException ex){
				//Fehler wenn Server nicht verfuegbar
				System.out.println("Server '"+host+"' on port '"+tcp+"' is not available!");
				setStop(false);
				client.closeUDP();
				return;
			}
			ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
			ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());

			//UDP Port an Server schicken
			outToServer.writeObject(new Messagepk((String.valueOf(udp))));

			Scanner inFromUser = new Scanner(System.in);
			ArrayList<String> inputLine;
			System.out.print(linestart);
			sentence = inFromUser.nextLine();


			while(isStop()){

				//Beende Client
				if((sentence.equals("!end"))){
					setStop(false);
					client.closeUDP();
					break;
				}
				else{
					boolean tryagain = true;
					boolean didalready=false;
					while(tryagain){
						Messagepk incomming = new Messagepk();
						try{
							//Sende Objekt an Server
							outToServer.writeObject(new Messagepk(sentence));
							//Warte auf Antwort von Server

							incomming = (Messagepk)(inFromServer.readObject());
							if(sentence.equals("!list")&&(incomming.getAuthenticationCode()!=null)&&(client.getKey()!=null)){
								if(client.verifyMessage(incomming)){
									tryagain=false;
								}
								else{
									if(didalready){
										tryagain=false;
									}
									else{
										didalready=true;
									}
								}
							}
							else{
								tryagain=false;
							}
						}
						catch (IOException e){
							System.out.println("Client disconnected from Server!");
							setStop(false);
							client.closeUDP();
							break;
						}
						//ArrayList der Message
						inputLine = incomming.getMessage();

						//ArrayList der Message auslesen
						for(int i =0;i<inputLine.size();i++){
							linestart = incomming.getUserLine();
							username=incomming.getUsername();
							System.out.println(inputLine.get(i));		
						}

						System.out.print(linestart);

					}
					sentence = inFromUser.nextLine();
				}

			}
			//Beenden der offenen Vebindung und Stream
			try{outToServer.close();}
			catch(SocketException e){/*Verbindung zum Host verloren;*/}
			try{inFromServer.close();}
			catch(SocketException e){/*Verbindung zum Host verloren;*/}
			try{clientSocket.close();}
			catch(SocketException e){/*Verbindung zum Host verloren;*/}

			//Scanner schliessen
			inFromUser.close();



			if(isStop()){
				//Schleife zusaetzlich fur Notifylistener beenden
				setStop(false);
				//Schliesse UDPSocket
				client.closeUDP();
			}

			//Schleife zusaetzlich fur Notifylistener beenden




		}
		else{
			//Usage
			System.out.println("Usage: Java Client <Host> <TCPPort> <UDPPort>");
		}

	}
	public void closeUDP(){
		//udpclientSocket.close();
	}
	@Override
	public void run() {

		//Oeffne neuen UDP Socket
		try {
			udpclientSocket= new DatagramSocket(udp);
		} catch (SocketException e) {
			System.out.println("UDP Port already in use");
			System.out.println("Please press enter to close the client");
			setStop(false);
			return;
		}

		while(isStop()){
			//erwartete Packeteigenschaften
			byte[] receiveData = new byte[1024];
			DatagramPacket receivePacket = new DatagramPacket(receiveData,receiveData.length);

			try {
				//Warte auf Packet
				udpclientSocket.receive(receivePacket);
				String receive = new String(receivePacket.getData());

				//Overhead des Packets wegschneiden
				char[] convert = new char[1024];
				convert = receive.toCharArray();
				receive="";
				for (int i=0;i<convert.length;i++){
					if(convert[i]>=32 && convert[i]<=126){
						receive+= String.valueOf(convert[i]);
					}
				}

				//Commands Uberpruefung
				SplittLine line = new SplittLine(receive);
				while(line.hasNext()){
					String notify = line.readNext();
					//Auktion wurde beendet
					if(notify.equals("!auction-ended")){
						String name = line.readNext();
						String hbit = line.readNext();
						String description=line.readNext();
						while (line.hasNext()){
							description +=" "+ line.readNext();
						}
						if(username.equals(name)){
							String message="The auction '"+description+"' has ended. You won with "+hbit+".";
							System.out.println(message);
							System.out.print(username+">");
						}
						else{
							System.out.println("The auction '"+description+"' has ended. "+name+" won with "+hbit+".");
							System.out.print(username+">");
						}
					}
					//Neue Betrag wurde auf Auktion geboten
					if(notify.equals("!new-bid")){
						String description = line.readNext();
						while (line.hasNext()){
							description +=" "+ line.readNext();
						}
						System.out.println("You have been overbid on '"+description+"'");
						System.out.print(username+">");
					}
					//Shutdownnotification vom Server
					if(notify.equals("!shutdown")){
						System.out.println("Server is not available anymore!");
						System.out.println("Please press enter to close the client");
					}
				}


			} catch (IOException e) {/*Verbindung zum Host verloren;*/}
		}



	}
	//Parametercheck
	public static boolean chkparam(String host, String tcpport, String udpport){
		if (host.equals("")||tcpport.equals("")||udpport.equals("")){
			return false;
		}
		try{
			int i = Integer.parseInt(tcpport);
			i = Integer.parseInt(udpport);
		}
		catch(NumberFormatException e){
			return false;
		}
		return true;

	}

	public static boolean isStop() {
		return stop;
	}


	public static void setStop(boolean stop) {
		Client.stop = stop;
	}
	
	
	//LAB 3 STAGE 2!!_______________________________________________________________________
	
	
	public Key getKey(){
		return key;
	}
	public boolean verifyMessage(Messagepk vm){
		ArrayList<String> messagearraylist = vm.getMessage();
		String appendmessage="";
		for(int i =0; i<messagearraylist.size();i++){
			appendmessage+=messagearraylist.get(i);
		}
		byte[] generatedhmac = createHMAC(appendmessage);

		return MessageDigest.isEqual(generatedhmac,vm.getAuthenticationCode());
	}

	private void readKeyfile(String username){

		byte[] keyBytes = new byte[1024];
		String pathToSecretKey = "keys/"+username+".key";
		FileInputStream fis;
		try {
			fis = new FileInputStream(pathToSecretKey);
			fis.read(keyBytes);
			fis.close();
		} catch (FileNotFoundException e1) {
			System.out.println("No secretkey found for user "+username);
		}
		catch (IOException e) {
			System.out.println("Error: Reading file");
		}
		byte[] input = Hex.decode(keyBytes);



		// make sure to use the right ALGORITHM for what you want to do
		// (see text)
		key = new SecretKeySpec(input,"HmacSHA256"); 
	}
	private byte[] createHMAC(String plain){
		byte[]plainarray = plain.getBytes();
		Mac hMac;
		try {
			hMac = Mac.getInstance("HmacSHA256");
			hMac.init(key);
			hMac.update(plainarray);
			return hMac.doFinal();

		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		// MESSAGE is the message to sign in bytes

		return null;
	}

}